"""Markdown document processor"""

import os
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, Type

from loguru import logger

from .body.base_body import BaseBody
from .error_handler import (
    ErrorLevel,
    FileSystemError,
    ValidationError,
    error_handler,
)
from .front_matter.base import BaseFrontMatter
from .front_matter.wechat import WeChatFrontMatter
from .image.reference import ImageReference
from .openrouter_service import OpenRouterService
from .util import is_valid_dir, is_valid_file


@dataclass
class MarkdownDocumentConfig:
    """配置类，包含所有需要的初始化参数"""

    source_dir: str
    relative_file_path: str
    content: str = ""
    front_matter_class: Optional[Type[BaseFrontMatter]] = None
    publish_type: str = "mdtool"


class MarkdownDocumentBuilder:
    """Markdown文档构建器"""

    def __init__(self):
        self._config = None
        self._content = None
        self._front_matter = None
        self._body = None

    def with_config(self, config: MarkdownDocumentConfig) -> "MarkdownDocumentBuilder":
        """设置基本配置"""
        self._config = config
        return self

    def load_content(self) -> "MarkdownDocumentBuilder":
        """加载文档内容"""
        if not self._config.content:
            file_path = Path(self._config.source_dir) / self._config.relative_file_path
            try:
                self._config.content = file_path.read_text(encoding="utf-8")
            except Exception as e:
                error_handler.handle_error(e, level=ErrorLevel.ERROR)
        return self

    def detect_front_matter(self) -> "MarkdownDocumentBuilder":
        """检测front matter类型"""
        # 确保 _config 已经被初始化
        if not hasattr(self, "_config") or self._config is None:
            raise ValueError("_config 属性未初始化")

        # 确保 front_matter_class 属性存在
        if not hasattr(self._config, "front_matter_class"):
            raise AttributeError("_config 对象缺少 front_matter_class 属性")
        # 确保 content 属性存在
        if not hasattr(self._config, "content"):
            raise AttributeError("_config 对象缺少 content 属性")

        if not self._config.front_matter_class:
            self._config.front_matter_class = self._detect_front_matter_type(
                self._config.content
            )
            if self._config.front_matter_class is None:
                raise ValidationError(
                    "Invalid document format: Document must contain two delimiters",
                    ErrorLevel.ERROR,
                )
        return self

    def parse_content(self) -> "MarkdownDocumentBuilder":
        """解析文档内容"""
        front_matter_text, body_text = self._seperate_front_matter_and_body(
            self._config.content, self._config.front_matter_class.DELIMITER
        )
        self._front_matter = self._config.front_matter_class.from_text(
            front_matter_text
        )
        self._front_matter._source_dir = self._config.source_dir
        self._body = BaseBody(body_text, self._config.source_dir)
        return self

    def build(self) -> "MarkdownDocument":
        """构建最终的MarkdownDocument对象"""
        doc = MarkdownDocument.__new__(MarkdownDocument)  # 创建未初始化的实例
        doc._source_dir = self._config.source_dir
        doc._relative_file_path = self._config.relative_file_path
        doc._abs_path = Path(doc._source_dir) / doc._relative_file_path
        doc.raw_content = self._config.content
        doc.front_matter_class = self._config.front_matter_class
        doc.front_matter = self._front_matter
        doc.body = self._body
        doc._openrouter = OpenRouterService()
        doc.image_uploaded = False
        doc.uploaded_images = {}
        doc.publish_type = self._config.publish_type  # 设置发布类型
        return doc

    @staticmethod
    def _detect_front_matter_type(content: str) -> Type[BaseFrontMatter]:
        """Detect front matter type based on delimiter"""
        for (
            delimiter,
            front_matter_class,
        ) in MarkdownDocument.FRONT_MATTER_TYPES.items():
            if content.find(delimiter) != -1:
                return front_matter_class
        return None

    @staticmethod
    def _seperate_front_matter_and_body(
        content: str, delimiter: str
    ) -> tuple[str, str]:
        """Split document into front matter and content parts"""
        parts = content.split(delimiter, 2)  # 最多分割2次，得到3个部分
        count = len(parts)

        if count == 1:  # 没有分隔符
            raise ValidationError(
                "Invalid document format: Document must contain two delimiters",
                ErrorLevel.ERROR,
            )
        elif count == 2:  # 一个分隔符
            front_matter = parts[0].strip()
            body_text = parts[1].strip()
            return front_matter, body_text
        else:  # 两个或更多分隔符，只使用前两个
            front_matter = parts[1].strip()
            body_text = parts[2].strip()
            return front_matter, body_text


class MarkdownDocument:
    """Class for handling markdown documents with front matter

    Attributes:
        _source_dir (str): Absolute path to the source directory containing the markdown file
        _file_path (str): Relative path (filename) of the markdown file within the source directory
        publish_type (str): Type of publishing target (mdtool.hugo)
    """

    FRONT_MATTER_TYPES = {"+++\n": WeChatFrontMatter}

    body: BaseBody
    front_matter: BaseFrontMatter
    _openrouter: OpenRouterService
    _source_dir: str
    _relative_file_path: str
    _abs_path: Path
    raw_content: str
    front_matter_class: Type[BaseFrontMatter]
    publish_type: str
    web_images_downloaded: bool = False
    uploaded_images = {}
    image_uploaded: bool = False

    @classmethod
    def from_file(
        cls,
        relative_file_path: str | Path,
        source_dir: Optional[str | Path],
        publish_type: str = "mdtool",
    ) -> "MarkdownDocument":
        """Create MarkdownDocument from file

        Args:
            relative_file_path: Path to markdown file
            source_dir: Optional source directory path. If not provided, will use the parent directory of file_path
            publish_type: Type of publishing target (mdtool.hugo)

        Returns:
            MarkdownDocument instance

        Raises:
            FileSystemError: If file or directory operations fail
            ValidationError: If file path validation fails
        """
        if not is_valid_dir(source_dir):
            raise FileSystemError(
                f"Source directory {source_dir} does not exist or is not a directory or is not absolute.",
                ErrorLevel.ERROR,
            )
        file_path = Path(source_dir) / relative_file_path

        if not is_valid_file(file_path):
            raise ValidationError(
                f"Relative file path {relative_file_path} is not valid.",
                ErrorLevel.ERROR,
            )
        if Path(relative_file_path).is_absolute():
            raise ValidationError(
                f"Relative file path {relative_file_path} is not relative.",
                ErrorLevel.ERROR,
            )

        config = MarkdownDocumentConfig(
            source_dir=str(source_dir),
            relative_file_path=str(relative_file_path),
            publish_type=publish_type,  # 添加发布类型
        )

        return (
            MarkdownDocumentBuilder()
            .with_config(config)
            .load_content()
            .detect_front_matter()
            .parse_content()
            .build()
        )

    def __init__(
        self,
        source_dir: str = "",
        relative_file_path: str = "",
        content: str = "",
        publish_type: str = "mdtool",
    ):
        """Initialize markdown document

        Args:
            source_dir (str): Absolute path to the source directory containing the markdown file
            relative_file_path (str): Relative path (filename) of the markdown file within the source directory
            content (str): Raw markdown content with front matter
            publish_type (str): Type of publishing target (mdtool.hugo)
        """
        config = MarkdownDocumentConfig(
            source_dir=source_dir,
            relative_file_path=relative_file_path,
            content=content,
            publish_type=publish_type,  # 添加发布类型
        )

        doc = (
            MarkdownDocumentBuilder()
            .with_config(config)
            .detect_front_matter()
            .parse_content()
            .build()
        )

        # Copy all attributes from the built document
        for attr, value in vars(doc).items():
            setattr(self, attr, value)

    def is_draft(self) -> bool:
        return self.front_matter.draft

    def auto_completion(self) -> None:
        """Enhance document using AI services.
        在发布之前调用补全功能"""
        self.front_matter.auto_completion(self.body.to_string(), self._openrouter)

    def _validate_paths(self, source_dir: str, relative_file_path: str) -> None:
        if not is_valid_dir(source_dir):
            raise FileSystemError(
                f"Source directory {source_dir} does not exist or is not a directory or is not absolute.",
                ErrorLevel.ERROR,
            )
        md_file_path = os.path.join(source_dir, relative_file_path)
        if not is_valid_file(md_file_path):
            raise FileSystemError(
                f"File {md_file_path} does not exist", ErrorLevel.ERROR
            )

    # Adapter properties to match MarkdownFile interface
    @property
    def source_dir(self) -> str:
        """Get source directory (adapter property)"""
        return self._source_dir

    @source_dir.setter
    def source_dir(self, value: str):
        """Set source directory (adapter property)"""
        self._source_dir = value

    @property
    def base_name(self) -> str:
        """Get base name of the file (adapter property)"""
        if not self._relative_file_path:
            return ""
        return os.path.basename(self._relative_file_path)

    @property
    def abs_path(self) -> str:
        """Get absolute path of the file (adapter property)"""
        return self.get_full_path()

    @property
    def title(self) -> str:
        return self.front_matter.title

    @property
    def content(self) -> str:
        """Get content of the file (adapter property)"""
        return self.to_string()

    @property
    def image_pairs(self) -> List[ImageReference]:
        """Get image references (adapter property)"""
        return self.get_image_references()

    def check_format(self) -> list[str]:
        """Check the format of the document"""
        result = self.front_matter.check_format()
        result.extend(self.body.check_format())
        logger.debug(self._relative_file_path + ":")
        if len(result) > 0:
            for r in result:
                logger.warning(f"\tformat errors:\t{r}")
            return result
        else:
            logger.debug(f"\tformat ok: {self._relative_file_path}")
            return []

    def check_images(self, auto_optimize: bool = False) -> bool:
        image_refs = self.get_image_references()
        if not image_refs:
            return True

        all_valid = True
        for image in image_refs:
            # 设置图片的发布类型
            image.publish_type = self.publish_type

            # 检查图片，如果需要则进行优化
            if not image.check(auto_optimize):
                all_valid = False

        return all_valid

    def image_in_text(self) -> List[str]:
        """Get image references in text format (adapter property)"""
        image_refs = self.get_image_references()
        if len(image_refs) == 0:
            print("no image in text")
            return []
        ret = []
        for img_ref in image_refs:
            ret.append(img_ref.url_in_text)
        return ret

    def get_image_references(self) -> List[ImageReference]:
        """Get all image references in the markdown document.

        Returns:
            List[ImageReference]: List of ImageReference objects for both banner and body images
        """
        image_refs = []

        # Get banner image if present
        banner_ref = self.front_matter.getBannerImgRef()
        if banner_ref:
            banner_ref.publish_type = self.publish_type  # 设置发布类型
            image_refs.append(banner_ref)

        # Get body images
        body_refs = self.body.get_image_references()
        if body_refs:
            for ref in body_refs:
                ref.publish_type = self.publish_type  # 设置发布类型
            image_refs.extend(body_refs)

        if not image_refs:
            logger.debug("No images found in markdown document")

        return image_refs

    def get_full_path(self) -> str:
        """Get the full path of the markdown file

        Returns:
            str: Full path of the markdown file

        Raises:
            ValidationError: If source directory or relative file path is not set
        """
        if not self._source_dir or not self._relative_file_path:
            raise ValidationError(
                "Source directory or relative file path is not set", ErrorLevel.ERROR
            )
        return str(Path(self._source_dir) / self._relative_file_path)

    def to_string(self) -> str:
        """Convert document back to string format

        Returns:
            Complete markdown document as string with proper formatting:
            1. First line: delimiter with newline
            2. Front matter content
            3. Delimiter with newline
            4. Empty line
            5. Document content
        """
        # Get front matter content and ensure it ends with a newline
        front_matter_str = self.front_matter.to_string().rstrip()

        # Format with delimiters and proper spacing
        parts = [
            self.front_matter_class.DELIMITER.rstrip(),  # First delimiter
            front_matter_str,  # Front matter content
            self.front_matter_class.DELIMITER.rstrip(),  # Second delimiter
            "",  # Empty line
            self.body.to_string().rstrip(),  # Document content
        ]

        # Join with newlines and ensure proper line endings
        return "\n".join(parts) + "\n"
